#include <iostream>
#include <OgreConfigFile.h>
#include <OgreRenderWindow.h>
#include <OgreRoot.h>
#include <OgreWindowEventUtilities.h>
#include "OgreApplication.h"

Ogre::SceneManager* OgreApplication::mpsSceneMgr = NULL;

OgreApplication::OgreApplication(void)
{
  mpRoot = NULL;
  mpCamera = NULL;
  mpViewport = NULL;
  mpWindow = NULL;

  // Default settings
#ifdef _DEBUG
  setPluginsPath("./plugins_d.cfg");  // Debug plugins
#else
  setPluginsPath("./plugins.cfg");    // Release plugins
#endif
  setResourcePath("./resources.cfg");
  setConfigPath("./ogre.cfg");
  setSceneManagerParams("DefaultScene", Ogre::ST_GENERIC);
  setDefaultCameraParams("DefaultCamera", Ogre::Vector3(0, 0, 500),
    Ogre::Vector3(0, 0, -100));
}


OgreApplication::~OgreApplication(void)
{
  delete mpRoot;
  mpRoot = NULL;
}

void OgreApplication::setPluginsPath(const Ogre::String& path)
{
  mPluginsPath = path;
}

void OgreApplication::setResourcePath(const Ogre::String& path)  
{
  mResourcePath = path;
}

void OgreApplication::setConfigPath(const Ogre::String& path)
{
  mConfigPath = path;
}

void OgreApplication::setSceneManagerParams(
  const Ogre::String& name, int type)
{
  mSceneMgrType = type;
  mSceneMgrName = name;
}

void OgreApplication::setDefaultCameraParams(
  const Ogre::String& name, const Ogre::Vector3& pos,  
  const Ogre::Vector3& lookAt)
{
  mDefaultCameraName = name;
  mDefaultCameraPos = pos;
  mDefaultCameraLookAt = lookAt;
}


bool OgreApplication::setup(void)
{
  bool success = false;

  if (!mpRoot)
  {
    try
    {
      // Create the Root object
      mpRoot = OGRE_NEW Ogre::Root(mPluginsPath, mConfigPath);

      // Setup resources
      setupResources();

      // Try to load the ogre config
      if (!configure())
      {
        delete mpRoot;
        mpRoot = NULL;
        success = false;
      }
      else
      {
        chooseSceneManager();
        setupCamera();
        createResourceListener();
        loadResources();
        initScene();
        success = true;
      }

    } catch (const Ogre::Exception& e)
    {
      std::cout << "Ogre Exception: " << e.getFullDescription() << std::endl;
      delete mpRoot;
      mpRoot = NULL;
      success = false;
    } catch (...)
    {
      std::cout << "Unknown Exception Caught" << std::endl;
      delete mpRoot;
      mpRoot = NULL;
      success = false;
    }
  }

  return success;
}

void OgreApplication::beginRenderLoop(void)
{
  if (mpRoot)
    mpRoot->startRendering();
}

void OgreApplication::renderOneFrame(void)
{
  static clock_t lastClock = clock();
  float tDiff = (clock()-lastClock)/1000.0f;
  lastClock = clock();

  // This will update all registered RenderWindows
  Ogre::WindowEventUtilities::messagePump();

  // Fire frameStarted, render a frame, and then fire frameEnded
  if (mpRoot)
  {
    mpRoot->_fireFrameStarted();
    update(tDiff);
    mpRoot->renderOneFrame();
    mpRoot->_fireFrameEnded();
  }
}

void OgreApplication::setupResources(void)
{
  // Load resource paths from config file
  Ogre::ConfigFile cf;

  // Go through all sections & settings in the file
  Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

  Ogre::String secName, typeName, archName;
  while (seci.hasMoreElements())
  {
    secName = seci.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator i;
    for (i = settings->begin(); i != settings->end(); ++i)
    {
      typeName = i->first;
      archName = i->second;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
      // OS X does not set the working directory relative to the app,
      // In order to make things portable on OS X we need to provide
      // the loading with it's own bundle path location
      if (!StringUtil::startsWith(archName, "/", false)) // only adjust relative dirs
        archName = String(macBundlePath() + "/" + archName);
#endif
      Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        archName, typeName, secName);

    }
  }
}

bool OgreApplication::configure(void)
{
  // Show the configuration dialog and initialise the system
  if (mpRoot->restoreConfig() || mpRoot->showConfigDialog())
  {
    // If returned true, user clicked OK so initialise
    // Here we choose to let the system create a default rendering window by passing 'true'
    mpWindow = mpRoot->initialise(true);
    return true;
  }
  else
  {
    return false;
  }
}

void OgreApplication::chooseSceneManager(void)
{
  // Create the SceneManager
  mpsSceneMgr = mpRoot->createSceneManager(
    static_cast<Ogre::SceneTypeMask>(mSceneMgrType), mSceneMgrName);
}

void OgreApplication::setupCamera(void)
{
  if (mpsSceneMgr && mpWindow)
  {
    // Create the Camera
    mpCamera = mpsSceneMgr->createCamera(mDefaultCameraName);
    mpCamera->setPosition(mDefaultCameraPos);
    mpCamera->lookAt(mDefaultCameraLookAt);
    mpCamera->setNearClipDistance(5);
    mpCamera->setAutoAspectRatio(true);

    // Create viewport
    mpViewport = mpWindow->addViewport(mpCamera);
    mpViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0));

    // Adjust aspect ratio
    float ratio = (float)mpViewport->getActualWidth() / 
      (float)mpViewport->getActualHeight();
    mpCamera->setAspectRatio(ratio);
  }
}

void OgreApplication::loadResources(void)
{
  // Load resource paths from config file
  Ogre::ConfigFile cf;
  cf.load(mResourcePath);

  // Go through all sections & settings in the file
  Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

  Ogre::String secName, typeName, archName;
  while (seci.hasMoreElements())
  {
    secName = seci.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator itr;
    for (itr = settings->begin(); itr != settings->end(); ++itr)
    {
      typeName = itr->first;
      archName = itr->second;
      Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        archName, typeName, secName);
    }
  }

  // Initialise, parse scripts etc
  Ogre::ResourceGroupManager& rm = Ogre::ResourceGroupManager::getSingleton();
  rm.initialiseAllResourceGroups();
}